package esrc.c9cf;

import java.util.ArrayList;

import javax.realtime.AbsoluteTime;
import javax.realtime.Clock;
import javax.realtime.NativeHelper;
import javax.realtime.RealtimeThread;
import javax.realtime.ReleaseParameters;
import javax.realtime.Schedulable;
import javax.realtime.SchedulingParameters;
import javax.realtime.RealtimeThread.RealtimeThreadState;

public abstract class Scheduler extends javax.realtime.Scheduler {

	protected static volatile ArrayList<Schedulable> feasibilitySet = new ArrayList<Schedulable>();
	protected static class Dispatcher {
		public static void Dispatch(RealtimeThread rtt, int cpu)
		{
			RealtimeThreadState state = rtt.getRealtimeThreadState();
			if(state == RealtimeThreadState.RTT_TERMINATED) throw new RuntimeException();
			//if(state == RealtimeThreadState.RTT_READY)
			{
				if(rtt.getCurrentCPU() != cpu)
				{
					rtt.setCurrentCPU(cpu);
					NativeHelper.sched_setaffinity(rtt.getTID(), cpu);
				}
				Thread rtt_thread = rtt.getThread();
				synchronized(rtt_thread)
				{
					rtt_thread.notify();
				}
			}
			
			/*
			//System.out.println("FS dump: size " + feasibilitySet.size());
			for(Schedulable schedulable : feasibilitySet)
			{
				System.out.print("" + (schedulable instanceof RealtimeThread?"R":"S"));
			}
			System.out.println();
			//System.out.print("\nEnd of dump\n");
			*/
			
			/*
			RealtimeThread rtt_preempted = null;
			int count = 0;
			RealtimeThreadState state = rtt.getRealtimeThreadState();
			if(state == RealtimeThreadState.RTT_TERMINATED) throw new RuntimeException();
			if(state == RealtimeThreadState.RTT_WAITING || state == RealtimeThreadState.RTT_NEW)
			{
				if(!(rtt.getAffinity().get(cpu))) throw new IllegalArgumentException();
				for(Schedulable schedulable : feasibilitySet)
				{
					if(schedulable instanceof RealtimeThread)
					{
						RealtimeThread rtt_temp = (RealtimeThread)schedulable;
						if(rtt_temp != rtt && rtt_temp.getCPU() == cpu && rtt_temp.getRealtimeThreadState() == RealtimeThreadState.RTT_RUNNING)
						{
							rtt_preempted = rtt_temp;
							count++;
						}
					}
				}
				if(count > 1) throw new RuntimeException("More than one threads running on a processor!");
				if(rtt_preempted != null)
				{
					rtt_preempted.setRealtimeThreadState(RealtimeThreadState.RTT_WAITING);
					if(NativeHelper.setpriority(rtt_preempted.getTID(), NativeHelper.getMinRTPriority()) != 0) throw new RuntimeException();
				}
				rtt.setRealtimeThreadState(RealtimeThreadState.RTT_RUNNING);
				if(!NativeHelper.sched_setaffinity(rtt.getTID(), cpu)) throw new RuntimeException();
				synchronized(rtt)
				{
					rtt.notify();
					//System.out.println("rescheduling " + rtt.getTID() + " thread");
				}
				if(NativeHelper.setpriority(rtt.getTID(), NativeHelper.getMaxRTPriority()) != 0) throw new RuntimeException();
			}
			*/
		}
	}
	
	protected Scheduler(){}
	public void reschedule()
	{
		reschedule(Clock.getRealtimeClock().getTime(), null);
	}
	public void reschedule(AbsoluteTime theTime)
	{
		reschedule(theTime, null);
	}
	public abstract void reschedule(AbsoluteTime theTime, Schedulable schedulable);
	public void addToFeasibilitySet(Schedulable schedulable)
	{
		synchronized(feasibilitySet)
		{
			if(!feasibilitySet.add(schedulable))  throw new IllegalArgumentException();
		}
	}
	@Override
	protected boolean addToFeasibility(Schedulable schedulable) {
		addToFeasibilitySet(schedulable);
		return isFeasible();
	}
	public void removeFromFeasibilitySet(Schedulable schedulable)
	{
		synchronized(feasibilitySet)
		{
			if(!feasibilitySet.remove(schedulable)) throw new IllegalArgumentException();
		}
	}
	@Override
	protected boolean removeFromFeasibility(Schedulable schedulable)
	{
		removeFromFeasibilitySet(schedulable);
		return isFeasible();
	}
	public boolean addIfFeasible(Schedulable schedulable)
	{
		addToFeasibilitySet(schedulable);
		if(isFeasible()) return true;
		else
			removeFromFeasibilitySet(schedulable);
		return false;
	}
	public boolean setIfFeasible(Schedulable schedulable, ReleaseParameters release)
	{
		return setIfFeasible(schedulable, schedulable.getSchedulingParameters(), release);
	}
	public boolean setIfFeasible(Schedulable schedulable, SchedulingParameters scheduling, ReleaseParameters release)
	{
		if(schedulable == null) throw new IllegalArgumentException();
		if(scheduling == null) scheduling = getDefaultSchedulingParameters();
		if(release == null) release = getDefaultReleaseParameters();
		
		RealtimeThread temp = new RealtimeThread(scheduling, release);
		boolean rtn;
		synchronized(feasibilitySet)
		{
			feasibilitySet.add(temp);
			rtn = isFeasible();
			feasibilitySet.remove(temp);
		}
		
		if(rtn)
		{
			schedulable.setSchedulingParameters(scheduling);
			schedulable.setReleaseParameters(release);	
		}
		return rtn;
	}
	
}
